home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_300
/
365_01
/
ctags.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-04-04
|
19KB
|
820 lines
/* ctags.c */
/* This is a reimplementation of the ctags(1) program. It supports ANSI C,
* and has heaps o' flags. It is meant to be distributed with elvis.
*/
#include <stdio.h>
#include "config.h"
#ifndef FALSE
# define FALSE 0
# define TRUE 1
#endif
#ifndef TAGS
# define TAGS "tags"
#endif
#ifndef REFS
# define REFS "refs"
#endif
#ifndef BLKSIZE
# define BLKSIZE 1024
#endif
#include "ctype.c" /* yes, that really is the .c file, not the .h one. */
/* -------------------------------------------------------------------------- */
/* Some global variables */
/* The following boolean variables are set according to command line flags */
int incl_static; /* -s include static tags */
int incl_types; /* -t include typedefs and structs */
int incl_vars; /* -v include variables */
int make_refs; /* -r generate a "refs" file */
int append_files; /* -a append to "tags" [and "refs"] files */
/* The following are used for outputting to the "tags" and "refs" files */
FILE *tags; /* used for writing to the "tags" file */
FILE *refs; /* used for writing to the "refs" file */
/* -------------------------------------------------------------------------- */
/* These are used for reading a source file. It keeps track of line numbers */
char *file_name; /* name of the current file */
FILE *file_fp; /* stream used for reading the file */
long file_lnum; /* line number in the current file */
long file_seek; /* fseek() offset to the start of current line */
int file_afternl; /* boolean: was previous character a newline? */
int file_prevch; /* a single character that was ungotten */
int file_header; /* boolean: is the current file a header file? */
/* This function opens a file, and resets the line counter. If it fails, it
* it will display an error message and leave the file_fp set to NULL.
*/
void file_open(name)
char *name; /* name of file to be opened */
{
/* if another file was already open, then close it */
if (file_fp)
{
fclose(file_fp);
}
/* try to open the file for reading. The file must be opened in
* "binary" mode because otherwise fseek() would misbehave under DOS.
*/
#if MSDOS || TOS
file_fp = fopen(name, "rb");
#else
file_fp = fopen(name, "r");
#endif
if (!file_fp)
{
perror(name);
}
/* reset the name & line number */
file_name = name;
file_lnum = 0L;
file_seek = 0L;
file_afternl = TRUE;
/* determine whether this is a header file */
file_header = FALSE;
name += strlen(name) - 2;
if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
{
file_header = TRUE;
}
}
/* This function reads a single character from the stream. If the *previous*
* character was a newline, then it also increments file_lnum and sets
* file_offset.
*/
int file_getc()
{
int ch;
/* if there is an ungotten character, then return it. Don't do any
* other processing on it, though, because we already did that the
* first time it was read.
*/
if (file_prevch)
{
ch = file_prevch;
file_prevch = 0;
return ch;
}
/* if previous character was a newline, then we're starting a line */
if (file_afternl)
{
file_afternl = FALSE;
file_seek = ftell(file_fp);
file_lnum++;
}
/* Get a character. If no file is open, then return EOF */
ch = (file_fp ? getc(file_fp) : EOF);
/* if it is a newline, then remember that fact */
if (ch == '\n')
{
file_afternl = TRUE;
}
/* return the character */
return ch;
}
/* This function ungets a character from the current source file */
void file_ungetc(ch)
int ch; /* character to be ungotten */
{
file_prevch = ch;
}
/* This function copies the current line out some other fp. It has no effect
* on the file_getc() function. During copying, any '\' characters are doubled
* and a leading '^' or trailing '$' is also quoted. The newline character is
* not copied.
*
* This is meant to be used when generating a tag line.
*/
void file_copyline(seek, fp)
long seek; /* where the lines starts in the source file */
FILE *fp; /* the output stream to copy it to */
{
long oldseek;/* where the file's pointer was before we messed it up */
char ch; /* a single character from the file */
char next; /* the next character from this file */
/* go to the start of the line */
oldseek = ftell(file_fp);
fseek(file_fp, seek, 0);
/* if first character is '^', then emit \^ */
ch = getc(file_fp);
if (ch == '^')
{
putc('\\', fp);
putc('^', fp);
ch = getc(file_fp);
}
/* write everything up to, but not including, the newline */
while (ch != '\n')
{
/* preread the next character from this file */
next = getc(file_fp);
/* if character is '\', or a terminal '$', then quote it */
if (ch == '\\' || (ch == '$' && next == '\n'))
{
putc('\\', fp);
}
putc(ch, fp);
/* next character... */
ch = next;
}
/* seek back to the old position */
fseek(file_fp, oldseek, 0);
}
/* -------------------------------------------------------------------------- */
/* This section handles preprocessor directives. It strips out all of the
* directives, and may emit a tag for #define directives.
*/
int cpp_afternl; /* boolean: look for '#' character? */
int cpp_prevch; /* an ungotten character, if any */
int cpp_refsok; /* boolean: can we echo characters out to "refs"? */
/* This function opens the file & resets variables */
void cpp_open(name)
char *name; /* name of source file to be opened */
{
/* use the lower-level file_open function to open the file */
file_open(name);
/* reset variables */
cpp_afternl = TRUE;
cpp_refsok = TRUE;
}
/* This function copies a character from the source file to the "refs" file */
void cpp_echo(ch)
int ch; /* the character to copy */
{
static wasnl;
/* echo non-EOF chars, unless not making "ref", or echo turned off */
if (ch != EOF && make_refs && cpp_refsok && !file_header)
{
/* try to avoid blank lines */
if (ch == '\n')
{
if (wasnl)
{
return;
}
wasnl = TRUE;
}
else
{
wasnl = FALSE;
}
/* add the character */
putc(ch, refs);
}
}
/* This function returns the next character which isn't part of a directive */
int cpp_getc()
{
static
int ch; /* the next input character */
char *scan;
/* if we have an ungotten character, then return it */
if (cpp_prevch)
{
ch = cpp_prevch;
cpp_prevch = 0;
return ch;
}
/* Get a character from the file. Return it if not special '#' */
ch = file_getc();
if (ch == '\n')
{
cpp_afternl = TRUE;
cpp_echo(ch);
return ch;
}
else if (ch != '#' || !cpp_afternl)
{
/* normal character. Any non-whitespace should turn off afternl */
if (ch != ' ' && ch != '\t')
{
cpp_afternl = FALSE;
}
cpp_echo(ch);
return ch;
}
/* Yikes! We found a directive */
/* see whether this is a #define line */
scan = " define ";
while (*scan)
{
if (*scan == ' ')
{
/* space character matches any whitespace */
do
{
ch = file_getc();
} while (ch == ' ' || ch == '\t');
file_ungetc(ch);
}
else
{
/* other characters should match exactly */
ch = file_getc();
if (ch != *scan)
{
file_ungetc(ch);
break;
}
}
scan++;
}
/* is this a #define line? and should we generate a tag for it? */
if (!*scan && (file_header || incl_static))
{
/* if not a header, then this will be a static tag */
if (!file_header)
{
fputs(file_name, tags);
putc(':', tags);
}
/* output the tag name */
for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
{
putc(ch, tags);
}
/* output a tab, the filename, another tab, and the line number */
fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
}
/* skip to the end of the directive -- a newline that isn't preceded
* by a '\' character.
*/
while (ch != EOF && ch != '\n')
{
if (ch == '\\')
{
ch = file_getc();
}
ch = file_getc();
}
/* return the newline that we f